Skip to content

iris-gui: App Store readiness, window-sizing overhaul, and zero-touch NVRAM/MAC setup#38

Merged
techomancer merged 22 commits into
techomancer:mainfrom
danifunker:iris-gui-sizing
Jun 18, 2026
Merged

iris-gui: App Store readiness, window-sizing overhaul, and zero-touch NVRAM/MAC setup#38
techomancer merged 22 commits into
techomancer:mainfrom
danifunker:iris-gui-sizing

Conversation

@danifunker

@danifunker danifunker commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

TL;DR

This branch makes the macOS GUI (iris-gui) shippable on the Mac App Store and
much friendlier out of the box. It bundles four themes of work:

  1. Mac App Store compliance — vendored winit blur-API patch; reviewer-testable
    camera + serial-console features; entitlement justifications.
  2. Window-sizing / layout overhaul — windowed-first, aspect-locked, left
    control column, adaptive framebuffer filtering, VM-screen scale slider.
  3. Zero-touch NVRAM + Ethernet-MAC handling — stable per-user NVRAM path, a
    seeded default NVRAM, and automatic MAC generation so IRIX comes up networked
    on first boot with no PROM-monitor steps.
  4. Managed absolute disk-image locations, plus an assortment of correctness
    and UX fixes (safe-stop on halt, framebuffer clear on restart, keyboard symbol
    mapping, mouse-capture scoping, JIT warning cleanup).

⚠️ Reviewer note on diff size

The raw diff is ~64k lines, but ~62k of that is a vendored, lightly-patched copy
of winit 0.30.13 under third_party/winit-0.30.13/. The actual project changes
are ~1,900 lines across 26 files. To review just those:

git diff --stat main...iris-gui-sizing -- . ':(exclude)third_party/winit-0.30.13/**'

The only edit to the vendored crate is a one-symbol stub (see §1.1); it is
otherwise an unmodified upstream checkout pinned via [patch.crates-io].

There is an open issue in winit tracking this progress and it has yet to be merged - rust-windowing/winit#4541 so this is why I'm proposing this fix.


1. Mac App Store compliance

1.1 Drop winit's private SkyLight blur API (guideline 2.5.1)

  • eframe's winit 0.30 calls the private API CGSSetWindowBackgroundBlurRadius in
    WindowDelegate::set_blur. IRIS never requests window blur, but the symbol is
    linked in regardless and Apple's static binary scan rejects it.
  • Fix: vendor a patched winit 0.30.13 (third_party/winit-0.30.13/) that stubs
    set_blur to a no-op and removes the private extern declarations, wired in
    via [patch.crates-io] in Cargo.toml.
  • Scope: only the 0.30.x requirement (eframe → egui-winit → glutin-winit) matches
    the patch. iris's own winit 0.29 dependency is the keyboard KeyCode type only
    and creates no window, so its blur code is dead-stripped.
  • Verified gone from the linked binary:
    nm -u target/release/iris-gui | grep CGSSetWindowBackgroundBlurRadius → empty.
  • Upstream tracking + migration path (winit #4205, milestone 0.31) documented in
    rules/macos/appstore-private-api.md.

1.2 Make camera + network.server entitlements reviewer-testable (guideline 2.4.5)

Both flagged entitlements are kept (product decision), but the build now ships
visible, reviewer-testable functionality that exercises each, and moves the one
socket-dependent action off the network:

  • Video-In → "Test Camera" (iris-gui/src/camera_test.rs): opens the host
    camera through the same CameraSource the IndyCam/VINO path uses, triggers the
    macOS camera prompt, and shows a live preview + capture status — demonstrating
    com.apple.security.device.camera.
  • Machine → "Serial console…" (iris-gui/src/serial_console.rs): an in-app
    viewer that connects to the emulator's loopback serial server
    (127.0.0.1:8881), streams the live IRIX console, and accepts typed input. The
    emulator listens (network.server) and the viewer connects (network.client)
    on loopback.
  • "Send IRIX halt" now types at the console in-process via a new z85c30
    channel-B injection queue (Machine::inject_serial_console, Cmd::HaltIrix)
    instead of opening a TcpStream to 8881, so clean shutdown no longer depends on
    the serial-server socket.
  • CameraSource now stops cleanly on drop (running flag + Drop join), so the
    Test Camera window releases the device; previously the capture thread leaked.
  • Help → "How camera & networking work…" opens an explainer window (end-user
    how-to plus the host-capability / entitlement rationale for App Review).
  • Entitlement comments rewritten to state the user-facing feature and direction
    (inbound vs outbound) for each key; paste-ready App Store Connect justifications
    and reviewer test steps in docs/appstore-review-response.md.

2. Window sizing / layout overhaul

2.1 Core (src/ui.rs) — aspect-ratio lock + correct default mode

  • Open the window at the Indy's real default video mode 1280×1024 (+ status bar)
    instead of 1024×768; the renderer snaps to the actual resolution via resize()
    once the PROM/IRIX programs its mode.
  • Lock the window to the emulated display's aspect ratio while resizing so the
    picture fills it without letterbox bars. New Ui::aspect_fit keeps whichever
    axis the user is actively dragging and derives the other; skips when
    fullscreen/maximized; 1-px tolerance prevents oscillation. Unit-tested.
  • New MachineConfig.lock_aspect_ratio (serde default true) lets non-standard
    monitors opt into free resizing (display then letterboxes).
  • display_res is published by the render thread (first frame + on mode change)
    for the event thread's aspect lock.

2.2 GUI (iris-gui) — windowed-first, left control column, scaling

  • Always open windowed; never restore a persisted fullscreen flag (F11 still
    toggles within a session).
  • Fit the window to the monitor on open: open hidden, fit on the first frame
    before revealing (frame-10 fallback + repaint pump so a missing monitor size
    can't leave it hidden).
  • Left control column (SidePanel) replaces the stacked top menu bar +
    toolbar + bottom status bar, giving the tall 5:4 display the full window height
    and using the spare horizontal space on widescreen monitors. Holds Start/Stop +
    save-state, the (now vertical) menus, Edit-config quick tabs, and a status
    footer (run-state, MIPS, machine name, dirty COW, toast).
  • ▶ disclosure arrows on the File/Machine/Memory/SCSI/View/Help buttons so they
    read as drop-downs.
  • VM-screen scale slider (View menu, 0.5×–3× in ¼ steps), independent of UI
    scale; the window resizes to the chosen scale (clamped to the monitor). The
    status footer shows the live VM-screen scale next to MIPS (1× = native).
  • UI scale (zoom) now only grows the controls — the window widens to fit instead
    of squeezing the display.
  • Zero the CentralPanel inner margin so the emulated display reaches the window
    edges (dark fill kept for the aspect letterbox).
  • The VM launch no longer re-snaps the window: its size is latched at app load and
    the guest display is letterboxed into it; only the scale slider / UI zoom resize.

2.3 Adaptive framebuffer filtering (iris-gui/src/framebuffer.rs)

The REX3 display texture was always uploaded NEAREST, so at fractional
device-pixel scale (common once UI scale ≠ 100% or the window is freely resized)
some emulated pixels were doubled and others not — uneven glyph strokes.
framebuffer_panel now measures the actual device-pixel scale and uploads NEAREST
at integer scales (crisp 1×/2×/3×) and LINEAR otherwise. Only the emulator display
texture is affected; re-uploads only when the integer/fractional state flips, so
no per-frame cost.

2.4 Mouse/menu capture fixes

  • Capture the mouse only when the framebuffer image itself is clicked, so
    navigating menus over the display no longer eats the pointer.
  • Control sidebar is always visible (removed the fullscreen auto-hide that made
    the toolbar vanish).

3. Zero-touch NVRAM + Ethernet MAC

Goal: IRIX boots networked on the first Start with no PROM-monitor steps, and
behaves identically whether launched via cargo run or the bundled .app.

3.1 Stable per-user NVRAM path

The NVRAM path defaulted to a relative nvram.bin resolved against the process
working dir, so cargo run (repo root) and the bundled .app loaded different
(often blank) NVRAMs — which is why networking silently broke after changing how
the app launches IRIS. Now anchored to dirs::config_dir()/iris/nvram.bin (next
to gui.json; the OS maps it into the sandbox container). Relative paths are
migrated on load (best-effort copy of any existing cwd-relative file so boot env /
MAC carry forward). GUI-only; the core CLI default is unchanged.

3.2 Detect + auto-write the MAC at NVRAM 0x13a

The MAC is 6 RAW bytes at NVRAM offset 0x13a (SGI OUI 08:00:69 + 3), not the
colon-ASCII typed at setenv — so the old text-scanning detection always reported
"no MAC". Detection now reads 0x13a. On Start, if blank, a generated
08:00:69:xx:xx:xx is written straight into the file (only those 6 bytes; boot env
preserved; .bak backup first) before boot, so IRIX attaches ec0 on first boot.

3.3 Seed a default NVRAM on first run + "Reset NVRAM (fresh PRAM)"

Fresh installs — especially the bundled .app, whose working dir has nothing for
the migration to copy — booted a blank NVRAM (no boot env, no MAC). An embedded
default NVRAM (the repo's known-good one with the MAC zeroed) is written to the
configured path on Start when none exists; the auto-write (§3.2) then fills the
per-machine MAC, so the machine boots networked with zero steps. New Machine-menu
"Reset NVRAM (fresh PRAM)" restores the default + a fresh MAC (backs the old file
up to .bak). Adds iris-gui/assets/nvram-default.bin.

3.4 Remove the now-dead MAC-setup UX

Earlier iterations of this branch added a guided "no Ethernet MAC" modal, an
Esc-into-PROM boot guard, and a send_monitor_command rtc-save helper over :8888.
The auto-write path makes all of that unreachable, so it is removed — leaving one
clean path: detect the MAC at 0x13a, write a generated one if blank.

3.5 Absolute paths in the machine summary

A disk/PROM/NVRAM created with a relative path showed as a bare filename with no
hint of location. The summary now resolves PROM/NVRAM/drive paths to absolute
(relative joined to the process working dir, where the emulator actually looks).


4. Disk-image management

"Create blank HDD" defaulted to a relative scsiN.raw, so accepting the default
dropped the image in the process working dir (and a relative write fails under the
App Store sandbox). New disks now default to <data_dir>/disks/scsiN.raw
absolute, created on demand, mapped into the sandbox container so it needs no
permission prompt. The picker is seeded there; "Choose…" still lets the user put a
disk anywhere (which grants + bookmarks access on the sandboxed build). Same
absolute default for the New Machine blank disk.
(iris-gui/src/dialogs/{create_disk.rs,new_machine.rs})


5. Correctness & smaller UX fixes

  • Safe-stop on halt (safe_stop.rs, core machine.rs): new
    Machine::cpu_is_running() / Status.cpu_halted. When the CPU has halted (clean
    shutdown / soft power-off, or idle at the PROM), nothing is writing, so
    safe_stop::evaluate short-circuits to "safe" and skips the force-stop warning.
    Unit-tested (writable disk while running = unsafe; halted = safe).
  • Auto-release mouse/keyboard on halt: edge-triggered on the running→halted
    transition (reset at Start); a toast notes it, clicking the display re-captures.
    No Ctrl+Alt+Esc needed after a clean shutdown.
  • Clear the framebuffer on restart: the FrameSink is a persistent slot shared
    across runs and held the previous run's last frame on Stop→Start. New
    FrameSink::reset (worker-side on Cmd::Start) + dropping the GUI's cached
    texture shows the "waiting for first REX3 frame" placeholder until the new run
    renders.
  • Map Key::Pipe and Key::Questionmark: egui 0.29 reports the shifted
    symbol as its own Key variant; Pipe (Shift+) and Questionmark (Shift+/) were
    the only symbol keys missing from map_key, so typing | or ? sent no
    scancode. Mapped onto the Backslash/Slash physical keys.
  • JIT dead-code cleanup: drop the dead modified_gprs restore in the
    branch-likely delay-slot failure path (the post-loop flush stores every GPR
    unconditionally, so the value is never read) — fixes an unused_assignments
    warning.
  • Doc-comment cleanup: removed references to a private sibling repo from the
    GUI dialog doc-comments and generalized the mouse-integration design note
    (rules/gui/gui_mouse_integration.md) to describe the classic-Mac absolute-mouse
    pattern without naming a specific emulator.

Files (excluding vendored winit)

Core (src/): ui.rs · machine.rs · config.rs · z85c30.rs ·
camera.rs · camera_nokhwa.rs · camera_v4l.rs · jit/compiler.rs · main.rs

GUI (iris-gui/src/): main.rs (+881) · settings.rs ·
serial_console.rs (new) · camera_test.rs (new) · config_ui.rs ·
safe_stop.rs · handle.rs · input.rs · framebuffer.rs ·
dialogs/{create_disk.rs,new_machine.rs} · assets/nvram-default.bin (new)

Build / docs / rules: Cargo.toml · installer/iris-gui.entitlements ·
docs/appstore-review-response.md · rules/macos/appstore-private-api.md ·
rules/gui/gui_mouse_integration.md


Testing / verification

  • Unit tests: src/ui.rs (aspect_fit), iris-gui/src/safe_stop.rs (halt
    behavior).
  • App Store 2.5.1 symbol absence verified:
    nm -u target/release/iris-gui | grep CGSSetWindowBackgroundBlurRadius → empty.
  • cargo check clean (workspace + iris-gui).

Items that still warrant a real test boot before relying on them:

  • The directly-written MAC at 0x13a (§3.2) assumes the PROM reads those bytes
    without a checksum it would reject — confirm ec0 comes up on first boot from a
    generated MAC.
  • The first-run seeded-default NVRAM path on a clean sandbox container.

Risk / compatibility notes

  • The vendored winit patch only affects the eframe/0.30 window path on macOS; it
    is removable once winit 0.31 ships the upstream fix (tracked in
    rules/macos/appstore-private-api.md).
  • The new config field lock_aspect_ratio is #[serde(default)], so existing
    iris.toml / gui.json files load unchanged.
  • The NVRAM path change (§3.1) is GUI-only; the core CLI default (nvram.bin
    relative) is unchanged.

danifunker and others added 22 commits June 18, 2026 05:22
eframe's winit 0.30 calls the private SkyLight API
CGSSetWindowBackgroundBlurRadius in WindowDelegate::set_blur. IRIS never
requests window blur, but the symbol is linked in regardless and Apple's
static binary scan rejects it (guideline 2.5.1, submission 2ed07ab1).

Vendor a patched winit 0.30.13 (third_party/winit-0.30.13/) that stubs
set_blur to a no-op and removes the private extern declarations, wired in
via [patch.crates-io]. Only the 0.30 requirement (eframe -> egui-winit ->
glutin-winit) matches the patch; iris's own winit 0.29 dependency is the
keyboard KeyCode type only and creates no window in iris-gui, so its blur
code is dead-stripped.

Verified the symbol is gone from the linked binary:
  nm -u target/release/iris-gui | grep CGSSetWindowBackgroundBlurRadius  -> empty

Upstream tracking + migration path documented in
rules/macos/appstore-private-api.md (winit #4205, milestone 0.31).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Keep both flagged entitlements (per product decision) and add visible,
reviewer-testable functionality plus move the one socket-dependent action
off the network:

- Video-In -> Test Camera: opens the host camera via the same CameraSource
  the IndyCam/VINO source uses, triggers the macOS camera prompt, and shows
  a live preview + capture status. Demonstrates com.apple.security.device.camera.
  (iris-gui/src/camera_test.rs)
- Machine -> Serial console...: in-app viewer that connects to the emulator's
  loopback serial server (127.0.0.1:8881) and streams the live IRIX console
  (and accepts typed input). The emulator listens (network.server) and the
  viewer connects (network.client) on loopback. (iris-gui/src/serial_console.rs)
- "Send IRIX halt" now types at the console in-process via a new z85c30
  channel-B injection queue (Machine::inject_serial_console, Cmd::HaltIrix)
  instead of opening a TcpStream to 8881, so clean shutdown no longer depends
  on the serial server socket.
- CameraSource now stops cleanly on drop (running flag + Drop join) so the
  Test Camera window releases the device; previously the capture thread leaked.

Entitlement comments updated; paste-ready App Store Connect justifications and
reviewer test steps in docs/appstore-review-response.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Two display/UX fixes for running at non-integer scale on widescreen monitors:

- Framebuffer filtering: the REX3 display texture was always uploaded with
  NEAREST, so at a fractional device-pixel scale (common once the UI scale is
  not 100% or the window is freely resized) some emulated pixels were doubled
  and others not — glyph strokes like the "T" came out uneven. framebuffer_panel
  now measures the actual device-pixel scale and uploads fb_tex with NEAREST at
  integer scales (crisp 1x/2x/3x) and LINEAR (bilinear) otherwise. Only the
  emulator display texture is affected; the egui shell renders independently.
  Re-uploads only when the integer/fractional state flips, so no per-frame cost.

- Left control column: replace the stacked top menu bar + top toolbar + bottom
  status bar (three rows of vertical chrome) with a single left SidePanel, so
  the tall 5:4 emulated display gets the full window height — using the spare
  horizontal real estate on widescreen monitors. The column holds Start/Stop +
  save-state, the menus (now vertical drop-downs), the Edit-config quick tabs,
  and a status footer (run-state, MIPS, machine name, dirty COW, toast) pinned
  to the bottom. Fullscreen auto-hide now reveals on the left edge instead of
  the top. Window-snap math is orientation-agnostic, so native-scale sizing is
  unchanged. menu_bar/toolbar/status_bar refactored into menu_list /
  machine_controls / config_quick_buttons / run_state_label / status_block /
  control_panel.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Mark the File/Machine/Memory/SCSI/View/Help menu buttons as expandable in the
left control column with a trailing ▶, so they read as drop-downs rather than
plain buttons.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
In the branch-likely delay-slot failure path, `modified_gprs` was restored to
its pre-delay value, but the post-loop flush stores every GPR unconditionally
(all_modified = 0xFFFFFFFE), so that value is never read — the compiler flagged
it as an unused assignment. Remove the dead restore (and its now-unused
capture); the gpr/hi/lo restores stay because the flush does read those.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- Open the window hidden and fit it to the monitor on the first frame before
  revealing it, so it no longer opens at a default size and visibly resizes.
  Reveals one frame after the fit settles, with a frame-10 fallback + repaint
  pump so a missing monitor size can't leave it hidden.
- WINDOW_DEFAULT_SIZE → [1512, 1024] to match the left-column running layout
  (now only the hidden pre-fit fallback).
- Zero the CentralPanel inner margin so the emulated display reaches the window
  edges (keeps the dark fill for the aspect letterbox).
- Add a Help → Diagnostics section with Test Camera and Network test (serial
  console) entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- Help → "How camera & networking work…" opens an explainer window with a
  Camera (IndyCam) and a Networking section, each covering both how to use it
  (end users) and the host capability / entitlement rationale (App Review).
- Help → Diagnostics "Test Camera" and "Network test" are now always shown but
  enabled only while the Indy is running (disabled-hover prompts to start one).
- When the guest becomes safe to stop (cpu_halted — clean shutdown / IRIX
  halt), auto-release the captured mouse/keyboard (edge-triggered on the
  running→halted transition, reset at Start) so no Ctrl+Alt+Esc is needed; a
  toast notes it and clicking the display re-captures.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Remove references to the private sibling "snow" repo and Snow-by-name
comparisons from the GUI dialog doc-comments and the mouse-integration
rules note. (The toolbox/MacRoman port credit lives on the separate
bluescsi-toolbox branch.)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ixes

- Status footer shows the live VM-screen scale next to MIPS (1× = native).
- New View-menu "VM screen" slider (0.5×–3× in ¼ steps), independent of the
  UI scale. The window resizes to the chosen scale (clamped to the monitor);
  the slider is never written back, so dragging it always works and the
  readout reports the scale actually achieved.
- UI scale (zoom) now only grows the controls — the window widens to fit
  instead of squeezing the display.
- Always open windowed; never restore a persisted fullscreen flag (F11 still
  toggles fullscreen within a session).
- Control sidebar is always visible — removed the fullscreen auto-hide that
  made the toolbar vanish.
- Capture the mouse only when the framebuffer image itself is clicked, so
  navigating menus over the display no longer eats the pointer.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The FrameSink is a persistent slot shared across runs; on Stop→Start it still
held the previous run's last frame and seq, so a restarted machine showed the
stale screen until its first REX3 frame arrived. Reset the sink worker-side on
Cmd::Start (new FrameSink::reset) and drop the GUI's cached texture, so it
shows the "waiting for first REX3 frame" placeholder until the new run renders.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…methods)

The NVRAM path defaulted to a relative "nvram.bin", resolved against the process
working directory — so `cargo run` (repo root) and a bundled .app loaded
*different* (often blank, MAC-less) NVRAMs. That's why networking silently broke
after changing how the app launches IRIS: a MAC set in one file is absent in the
other, so IRIX never attaches ec0.

Anchor the GUI's NVRAM to dirs::config_dir()/iris/nvram.bin (next to gui.json;
the OS maps it into the sandbox container on the App Store build, so one code
path is correct for cargo-run and the bundled app). Relative paths are migrated
on load, best-effort copying any existing cwd-relative file so the PROM env
(boot settings, any MAC) carries forward. GUI-only; the core CLI default is
unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
On Start, if the NVRAM has no MAC (heuristic: no xx:xx:xx:xx:xx:xx ASCII in the
file — the PROM stores env values as text), show a movable modal that:
  - explains why IRIX networking / System Manager / Disk Manager fail,
  - offers a generated SGI-OUI MAC (08:00:69:xx:xx:xx, stable per machine name),
  - shows the exact `setenv -f eaddr <mac>` to run at the PROM monitor
    (selectable monospace), and
  - has a "Save NVRAM (rtc save)" button that drives the iris monitor on
    :8888 to persist nvram.bin.

GUI-only; the core's manual setenv/rtc-save workflow is unchanged. Avoids
hand-encoding the firmware-owned NVRAM env format — the real PROM still does the
encoding; we just guide the one command and handle persistence.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
When the NVRAM has no Ethernet MAC, IRIX booting first means the user sets the
MAC, then has to reboot — a wasteful double boot. Instead, interrupt the PROM
autoboot countdown: inject Esc into the guest keyboard on a cadence over the
first ~8s after a no-MAC Start (new input::tap_escape; gated by mac_guard_frames),
so the machine drops to the System Maintenance Menu. The user picks the Command
Monitor, runs the shown `setenv -f eaddr <mac>`, clicks Save NVRAM (rtc save),
and types `boot` — one IRIX boot, no reboot. The guided modal text now reflects
this flow, with a fallback note if the interrupt is missed.

Best-effort timing (depends on catching the countdown); worth a test boot.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…13a)

The MAC is stored as 6 RAW bytes at NVRAM offset 0x13a (SGI OUI 08:00:69 + 3),
not the colon-ASCII you type at `setenv` — so the old nvram_has_mac (scanning
for "xx:xx:xx:xx:xx:xx" text) always reported "no MAC". Fix detection to read
0x13a. On Start, if the NVRAM has no MAC, write a generated 08:00:69:xx:xx:xx
straight into the file (only those 6 bytes; boot env preserved; .bak backup
first) before boot — so IRIX attaches ec0 on the first boot, no PROM monitor,
no reboot.

Removes the fragile Esc-interrupt boot guard (the panic cause) and stops arming
the setenv prompt (now a latent, never-shown fallback). NOTE: assumes the PROM
reads the MAC from these bytes without a checksum it would reject — needs a test
boot to confirm ec0 comes up from a directly-written MAC.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
egui 0.29 reports the *shifted* symbol as its own Key variant, and Pipe (Shift+\)
and Questionmark (Shift+/) were the only symbol keys missing from map_key — so
typing | or ? sent no scancode to the guest at all. Map them onto the Backslash
and Slash physical keys (Shift is forwarded separately, so the guest's keymap
forms | and ?).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The auto-write path (write the MAC straight into NVRAM at 0x13a, validated to
bring up ec0) handles the no-MAC case silently, so remove the now-unreachable
guided-setenv modal (MacModal + its rendering) and send_monitor_command (the
rtc-save-over-:8888 helper). One clean path remains: detect the MAC at 0x13a,
write a generated one if blank.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
A disk created with a relative path (e.g. scsi3.raw) showed as a bare filename
with no hint of where it lives. Resolve PROM/NVRAM/drive paths to absolute (a
relative path is joined to the process working dir, where the emulator actually
looks) so the summary always shows the full path.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Fresh installs — especially the bundled .app, which has nothing in its working
dir for Stage 1's migration to copy — booted a blank NVRAM: no boot env, no MAC.
Embed a default NVRAM (the repo's known-good one with the MAC zeroed) and write
it to the configured path on Start when no NVRAM exists; the existing auto-write
then fills in a per-machine MAC, so the machine boots networked with zero steps.

Add a Machine-menu "Reset NVRAM (fresh PRAM)" that restores the default and
assigns a fresh MAC (backs the old file up to .bak). NVRAM remains per-machine
configurable via the config NVRAM field, so a machine can use its own file.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
"Create blank HDD" defaulted the filename to a relative scsiN.raw, so accepting
the default dropped the image in the process working dir (and a relative write
fails under the App Store sandbox). Default new disks to <data_dir>/disks/
scsiN.raw — absolute, created on demand, and mapped into the sandbox container
so it needs no permission prompt. The picker is seeded there, and "Choose…"
still lets the user put a disk anywhere (which grants + bookmarks access on the
sandboxed build). Same absolute default for the New Machine blank disk.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The window size is latched at app load (the saved size, or the
first-launch fit to vm_scale) and the guest display is letterboxed into
it. Only the VM-scale slider and UI zoom re-snap the window after that,
so Start no longer triggers a framebuffer snap.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@danifunker

Copy link
Copy Markdown
Contributor Author

I just rebased the branch against your most recent changes to main, seems to still be OK.

@techomancer techomancer merged commit f39fbe5 into techomancer:main Jun 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants